package com.alibaba.nacos.naming.core.v2.index;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.naming.core.v2.ServiceManager;
import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager;
import com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata;
import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager;
import com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo;
import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo;
import com.alibaba.nacos.naming.core.v2.pojo.Service;
import com.alibaba.nacos.naming.utils.InstanceUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class ServiceStorage {
    @Autowired
    private ServiceManager serviceManager;
    @Autowired
    private ClientManager clientManager;
    @Autowired
    private ClientServiceIndexesManager clientServiceIndexesManager;
    @Autowired
    private NamingMetadataManager namingMetadataManager;

    private final Map<Service, ServiceInfo> serviceDataIndexes = new ConcurrentHashMap<>();
    private final Map<Service, Set<String>> serviceClusterIndex = new ConcurrentHashMap<>();

    public ServiceInfo getPushData(Service service) {
        ServiceInfo result = new ServiceInfo();
        result.setName(service.getName()).setGroupName(service.getGroup()).setCacheMillis(TimeUnit.SECONDS.toMillis(10));

        Service usingService = serviceManager.getOrCreate(service);
        if (usingService == null) {
            return result;
        }

        List<Instance> allInstancesFromIndex = getAllInstancesFromIndex(service);
        result.setHosts(allInstancesFromIndex);
        serviceDataIndexes.put(service, result);
        return result;
    }

    public ServiceInfo getData(Service service) {
        // 如果已经缓存 从缓存中获取 否则即时计算
        ServiceInfo result = serviceDataIndexes.get(service);
        if (result != null) {
            return result;
        }
        return getPushData(service);
    }

    private List<Instance> getAllInstancesFromIndex(Service service) {
        Collection<String> allClientsRegisteredService = clientServiceIndexesManager.getAllClientsRegisteredService(service);
        Set<Instance> result = allClientsRegisteredService.stream()
                .map(clientManager::getClient)
                .filter(Objects::nonNull)
                .map(client -> client.getInstancePublishInfo(service))
                .flatMap(instancePublishInfo -> getInstanceStream(service, instancePublishInfo))
                .collect(Collectors.toSet());

        Set<String> clusters = result.stream().map(Instance::getClusterName).collect(Collectors.toSet());
        serviceClusterIndex.put(service, clusters);
        return new LinkedList<>(result);
    }

    private Stream<Instance> getInstanceStream(Service service,
                                               InstancePublishInfo instancePublishInfo) {
        if (instancePublishInfo instanceof BatchInstancePublishInfo) {
            throw new UnsupportedOperationException();
        } else {
            Instance instance = InstanceUtil.parseToApiInstance(service, instancePublishInfo);
            InstanceMetadata metadata = namingMetadataManager.getInstanceMetadata(service, instancePublishInfo.getMetadataId());
            Optional.ofNullable(metadata).ifPresent(instanceMetadata -> {
                // 更新元数据
            });
            return Stream.of(instance);
        }
    }
}
